/* Copyright (c) 2001, Nokia Mobile Phones. All rights reserved */

#include <msvids.h>
#include <mtclreg.h>
#include <mtclbase.h>
//#include <comabs.h>
#include <txtrich.h>
#include <smsclnt.h>
#include <smuthdr.h>
#include <smutset.h>
#include <smscmds.h>
#include "SmsSendHandler.h"
#include "MsvObserver.h"
#include <charconv.h> 	 // CCnvCharacterSetConverter
#include <eikenv.h>
#include "ProjectsDefines.h"
//#include <MuiuMsvSingleOpWatcher.h>

// this is the content of the message
// _LIT(KSMSText, "Example SMS");

CSmsSendHandler* CSmsSendHandler::NewL( MMsvObserver& aObserver )
{
    CSmsSendHandler* self = CSmsSendHandler::NewLC( aObserver );
    CleanupStack::Pop( );
    return self;
}

CSmsSendHandler* CSmsSendHandler::NewLC( MMsvObserver& aObserver )
{
    CSmsSendHandler* self = new ( ELeave ) CSmsSendHandler( aObserver );
    CleanupStack::PushL( self );
    self->ConstructL( );
    return self;
}

CSmsSendHandler::CSmsSendHandler( MMsvObserver& aObserver ) :
    CMsvHandler( aObserver ), iPhase( EIdle )
{
}

CSmsSendHandler::~CSmsSendHandler( )
{
    // No implementation required
}

TBool CSmsSendHandler::IsIdle( )
{
    if( CMsvHandler::IsIdle( ) && ( iPhase == EIdle ) )
    {
        return ETrue;
    }
    else
    {
        return EFalse;
    }
}

void CSmsSendHandler::ConstructL( )
{
    CMsvHandler::ConstructL( );
}

// from CActive
void CSmsSendHandler::RunL( )
{
    _ASSERT_LOG(iOperation);

    // Mtm's should always return KErrNone and clients should rely on other means
    // to check on operation progress
    User::LeaveIfError( iStatus.Int( ) );

    // Determine the current operations progress
    // Note : This class is only appropriate to Locally acting operations
    //        such as Create, Move, Delete.
    //        For the CBaseMtm::InvokeAsyncFunctionL, used to schedule the messange for sending,
    //        no status is available as this is not a local operation, so we use the fact
    //        that sent messages are moved to the 'Sent' folder which we can detect using
    //        the HandleSessionEventL
    TMsvLocalOperationProgress progress;
    TUid mtmUid = iOperation->Mtm( );
    if( mtmUid == KUidMsvLocalServiceMtm )
    {
        progress = McliUtils::GetLocalProgressL( *iOperation );
        User::LeaveIfError( progress.iError );
    }
    else
    {
        // The request to schedule the message is the only non-local operation
        // we request from this example
        if( iPhase != EWaitingForScheduled )
        {
            // User::Panic(KSms, ESmsStateError);
        }
    }

    delete iOperation;
    iOperation = NULL;

    switch( iPhase )
    {
        case EWaitingForCreate:
            iObserver.HandleStatusChange( MMsvObserver::ECreated );
            SetMtmEntryL( progress.iId );
            if( InitializeMessageL( ) )
            {
                // if ( MoveMessageEntryL(KMsvGlobalOutBoxIndexEntryId) )
                if( MoveMessageEntryL( KMsvGlobalInBoxIndexEntryId ) )
                {
                    iPhase = EWaitingForMove;
                }
                else
                {
                    iPhase = EIdle;
                }
            }
            else
            {
                iPhase = EIdle;
            }
            break;

        case EWaitingForMove:
            {
                // iObserver.HandleStatusChange(MMsvObserver::EMovedToOutBox);
                iObserver.HandleStatusChange( MMsvObserver::EMovedToInbox );
                // We must create an entry selection for message copies
                // (although now we only have one message in selection)
                CMsvEntrySelection* selection = new ( ELeave ) CMsvEntrySelection;
                CleanupStack::PushL( selection );

                selection->AppendL( progress.iId );

                // schedule the sending with the active scheduler
                SetScheduledSendingStateL( *selection );

                CleanupStack::PopAndDestroy( selection );
                iPhase = EWaitingForScheduled;
                break;
            }

        case EWaitingForScheduled:
            {
                const TMsvEntry& msvEntry = iMtm->Entry( ).Entry( );
                TInt state = msvEntry.SendingState( );
                if( state != KMsvSendStateScheduled )
                {
                    iObserver.HandleError( MMsvObserver::EScheduleFailed );
                    iPhase = EIdle;
                }
                else
                {
                    iObserver.HandleStatusChange( MMsvObserver::EScheduledForSend );
                    iPhase = EWaitingForSent;
                }
                break;
            }

        case EWaitingForDeleted:
            iObserver.HandleStatusChange( MMsvObserver::EDeleted );
            iPhase = EIdle;
            break;

        case EWaitingForSent: // We handle this in HandleSessionEventL
        case EIdle: // Shouldn't get triggered in this state
        default:
            _ASSERT_LOG(EFalse);
    }

}

void CSmsSendHandler::CreateNewMessageL( )
{
    TMsvEntry newEntry; // This represents an entry in the Message Server index
    newEntry.iMtm = KUidMsgTypeSMS; // message type is SMS
    newEntry.iType = KUidMsvMessageEntry; // this defines the type of the entry: message
    newEntry.iServiceId = KMsvLocalServiceIndexEntryId; // ID of local service (containing the standard folders)
    newEntry.iDate.HomeTime( ); // set the date of the entry to home time
    newEntry.iDate.UniversalTime( );
    newEntry.SetInPreparation( ETrue ); // a flag that this message is in preparation

    // - CMsvEntry accesses and acts upon a particular Message Server entry.
    // - NewL() does not create a new entry, but simply a new object to access an existing entry.
    // - It takes in as parameters the client's message server session,
    //   ID of the entry to access and initial sorting order of the children of the entry.
    CMsvEntry* entry = CMsvEntry::NewL( *iSession, KMsvDraftEntryIdValue, TMsvSelectionOrdering( ) );
    CleanupStack::PushL( entry );

    iOperation = entry->CreateL( newEntry, iStatus );
    CleanupStack::PopAndDestroy( entry );

    // The RunL of this class will trigger when the server completes the request
    SetActive( );
    iPhase = EWaitingForCreate;

}

void CSmsSendHandler::SendToL( const TDesC& aRecipient, const TDesC& aText )
{
    iRecipientsTelNum = aRecipient;
    iText = aText;
    CreateNewMessageL( );
}

TBool CSmsSendHandler::SetupSmsHeaderL( )
{
    if( iMtm )
    {
        // To handle the sms specifics we start using SmsMtm
        CSmsClientMtm* smsMtm = static_cast< CSmsClientMtm* > ( iMtm );
        //
        smsMtm->RestoreServiceAndSettingsL( );

        // CSmsHeader encapsulates data specific for sms messages,
        // like service center number and options for sending.
        CSmsHeader& header = smsMtm->SmsHeader( );
        CSmsSettings& serviceSettings = smsMtm->ServiceSettings( );

        // Added by XiaoGuo
        serviceSettings.SetCharacterSet( TSmsDataCodingScheme::ESmsAlphabetUCS2 );

        CSmsSettings* sendOptions = CSmsSettings::NewL( );
        CleanupStack::PushL( sendOptions );
        sendOptions->CopyL( serviceSettings ); // restore existing settings

        // set send options
        sendOptions->SetDelivery( ESmsDeliveryImmediately ); // set to be delivered immediately
        header.SetSmsSettingsL( *sendOptions );

        CleanupStack::PopAndDestroy( sendOptions );

        // let's check if there's a service centre address
        if( header.Message( ).ServiceCenterAddress( ).Length( ) == 0 )
        {
            if( serviceSettings.ServiceCenterCount( ) != 0 )
            {
                // set sc address to default.
                CSmsServiceCenter& sc = serviceSettings. GetServiceCenter( serviceSettings.DefaultServiceCenter( ) );
                header.Message( ).SetServiceCenterAddressL( sc.Address( ) );
            }
            else
            {
                // here there could be a dialog in which user can add sc number
                iObserver.HandleError( MMsvObserver::ENoServiceCentre );
                return EFalse;
            }
        }
        return ETrue;
    }
    else
    {
        return EFalse;
    }
}

#include <aknutils.h>
#include "ProjectsDefines.h"
const TUint KSmsEdUnicodeLFSupportedByBasicPhones = 0x000A;
void CSmsSendHandler::PopulateMessageL( TMsvEntry& aMsvEntry )
{
    _ASSERT_LOG(iMtm);

    // We get the message body from Mtm and insert a bodytext
    CRichText& mtmBody = iMtm->Body( );
    mtmBody.Reset( );

    mtmBody.InsertL( 0, iText() );

    // set iRecipientTelNum into the Details of the entry
    aMsvEntry.iDetails.Set( iRecipientsTelNum() ); // set recipient info in details
    aMsvEntry.SetInPreparation( EFalse );

    aMsvEntry.SetSendingState( KMsvSendStateWaiting );
    aMsvEntry.iDate.HomeTime( );
    aMsvEntry.iDate.UniversalTime( );
}

TBool CSmsSendHandler::InitializeMessageL( )
{
    _ASSERT_LOG(iMtm);

    TMsvEntry msvEntry = ( iMtm->Entry( ) ).Entry( );

    PopulateMessageL( msvEntry );

    // Set SMS specific Mtm fields
    if( !SetupSmsHeaderL( ) )
    {
        return EFalse;
    }

    // Add our recipient to the list, takes in two TDesCs, first is real address and second is an alias
    // works also without the alias parameter.
    iMtm->AddAddresseeL( iRecipientsTelNum(), msvEntry.iDetails );

    // save message
    CMsvEntry& entry = iMtm->Entry( );
    entry.ChangeL( msvEntry ); // commit index changes
    iMtm->SaveMessageL( ); // commit message to message server

    return ETrue;
}

TBool CSmsSendHandler::MoveMessageEntryL( TMsvId aTarget )
{
    _ASSERT_LOG( iMtm );

    TMsvEntry msvEntry( ( iMtm->Entry( ) ).Entry( ) );

    if( msvEntry.Parent( ) != aTarget )
    {
        TMsvSelectionOrdering sort;
        sort.SetShowInvisibleEntries( ETrue ); // we want to also handle the invisible entries
        // Take a handle to the parent entry
        CMsvEntry* parentEntry = CMsvEntry::NewL( iMtm->Session( ), msvEntry.Parent( ), sort );
        CleanupStack::PushL( parentEntry );

        // Move original from the parent to the new location
        iOperation = parentEntry->MoveL( msvEntry.Id( ), aTarget, iStatus );

        CleanupStack::PopAndDestroy( parentEntry );
        SetActive( );

        return ETrue;
    }

    return EFalse;
}

void CSmsSendHandler::SetScheduledSendingStateL( CMsvEntrySelection& aSelection )
{
    _ASSERT_LOG(iMtm);

    // Add entry to task scheduler
    TBuf8< 1 > dummyParams;
    // invoking async schedule copy command on our mtm
    iOperation = iMtm->InvokeAsyncFunctionL( ESmsMtmCommandScheduleCopy,
    // ESmsMtmCommandScheduleMove,
            aSelection, dummyParams, iStatus );
    SetActive( );
}

void CSmsSendHandler::HandleChangedEntryL( TMsvId aEntryId )
{
    // if we've no Mtm then we can't be in the middle of sending a message
    if( iMtm )
    {
        // Compare the entry supplied with our current context (the message
        // we have been dealing with)
        TMsvEntry msvEntry( ( iMtm->Entry( ) ).Entry( ) );
        if( msvEntry.Id( ) == aEntryId )
        {
#if 1
            if (msvEntry.SendingState() == KMsvSendStateSent)
            {
                iPhase = EWaitingForDeleted;
                iObserver.HandleStatusChange(MMsvObserver::ESent);
                DeleteEntryL(msvEntry);
            }
            else if (msvEntry.SendingState() == KMsvSendStateFailed)
            {
                iPhase = EIdle;
                iObserver.HandleError(MMsvObserver::ESendFailed);
            }
#endif
        }
    }
}

void CSmsSendHandler::HandleSessionEventL( TMsvSessionEvent aEvent, TAny* aArg1, TAny* /*aArg2*/, TAny* /*aArg3*/)
{
    switch( aEvent )
    {
        case EMsvEntriesChanged:
            {
                // Find out what happened to the message we scheduled for sending
                if( iPhase == EWaitingForSent )
                {
                    // We take the moved entries into a selection
                    CMsvEntrySelection* entries = static_cast< CMsvEntrySelection* > ( aArg1 );

                    //Process each created entry, one at a time.
                    for( TInt i = 0; i < entries->Count( ); i++ )
                    {
                        HandleChangedEntryL( entries->At( i ) );
                    }
                    iPhase = EIdle;
                }
            }
            break;

            // This event tells us that the session has been opened
        case EMsvServerReady:
            CompleteConstructL( ); // Construct the mtm registry
            break;

        case EMsvCloseSession:
            // Handle close session
            iSession->CloseMessageServer( );
            break;

        case EMsvServerTerminated:
            // Handle server terminated
            iSession->CloseMessageServer( );
            break;

        case EMsvGeneralError:
        case EMsvServerFailedToStart:
            // A major problem has occurred
            iObserver.HandleError( MMsvObserver::EFatalServerError );
            break;

        default:
            // All other events are ignored
            break;
    }
}

